/**
    @file util.cpp
    @brief Helper functions
*/
#include "util.hpp"

std::vector<std::string> pm3::util::tokenize( const std::string &r_string, const std::string &r_delims ) {
	std::vector<std::string> tokl;
    size_t last_pos, pos;

	// Skip delimiters at beginning
    last_pos = r_string.find_first_not_of( r_delims, 0 );
	// Contains only delimiters?
	if (last_pos == std::string::npos)
		return tokl;

	// Find the first delimiter
    pos = r_string.find_first_of( r_delims, last_pos );
	
    while (pos != std::string::npos || last_pos != std::string::npos) {
        // Found a token, add it to the vector
        tokl.push_back( r_string.substr( last_pos, pos - last_pos ) );

        // Skip delimiters
        last_pos = r_string.find_first_not_of( r_delims, pos );
		
        // Find the next delimiter
        pos = r_string.find_first_of( r_delims, last_pos );
    }

	return tokl;
}

int pm3::util::str_to_directions( const std::string &r_string ) {
    int dirs = 0;

    for (size_t i=0; i<r_string.size(); i++) {
        switch( r_string [i] ) {
            case 'N':
                dirs |= pm3::util::D_N;
                break;
            case 'E':
                dirs |= pm3::util::D_E;
                break;
            case 'S':
                dirs |= pm3::util::D_S;
                break;
            case 'W':
                dirs |= pm3::util::D_W;
                break;
            case 'U':
                dirs |= pm3::util::D_U;
                break;
            case 'D':
                dirs |= pm3::util::D_D;
                break;
            default: break;
        }
    }

    return dirs;
}

std::string pm3::util::directions_to_str( int i_directions ) {
    std::string str;

    if (( i_directions & pm3::util::D_N ) != 0)
        str += "N";
    if (( i_directions & pm3::util::D_E ) != 0)
        str += "E";
    if (( i_directions & pm3::util::D_S ) != 0)
        str += "S";
    if (( i_directions & pm3::util::D_W ) != 0)
        str += "W";
    if (( i_directions & pm3::util::D_U ) != 0)
        str += "U";
    if (( i_directions & pm3::util::D_D ) != 0)
        str += "D";

    return str;
}

int pm3::util::get_rand_direction( int i_directions ) {
    int num = 0;
    int rnd = 0;
    int i;

    // Count the number of directions in total
    for (i=1; i<=pm3::util::D_LAST; i<<=1) {
        if (( i_directions & i ) != 0)
            num++;
    }

    // Find a random index
    rnd = rand() % num;

    // Find the direction that corresponds to the index
    for (i=1,num=0; i<=pm3::util::D_LAST; i<<=1) {
        if (( i_directions & i ) != 0) {
            // Found the direction?
            if (num == rnd)
                return i;

            num++;
        }
    }

    return D_NONE;
}

int pm3::util::reverse_directions( int i_directions ) {
    int reverse = 0;

    if (( i_directions & D_N ) != 0)
        reverse |= D_S;
    if (( i_directions & D_E ) != 0)
        reverse |= D_W;
    if (( i_directions & D_S ) != 0)
        reverse |= D_N;
    if (( i_directions & D_W ) != 0)
        reverse |= D_E;
    if (( i_directions & D_U ) != 0)
        reverse |= D_D;
    if (( i_directions & D_D ) != 0)
        reverse |= D_U;

    return reverse;
}

int pm3::util::get_direction_id( int i_dir ) {
    if (( i_dir & D_N ) != 0)
        return DID_N;
    if (( i_dir & D_E ) != 0)
        return DID_E;
    if (( i_dir & D_S ) != 0)
        return DID_S;
    if (( i_dir & D_W ) != 0)
        return DID_W;
    if (( i_dir & D_U ) != 0)
        return DID_U;
    if (( i_dir & D_D ) != 0)
        return DID_D;
    return DID_N;
}

Ogre::Quaternion pm3::util::direction_to_quat( int i_direction ) {
    Ogre::Vector3 vx, vy, vz;
    Ogre::Quaternion quat;

    if (( i_direction & pm3::util::D_N ) != 0) {
        vx = Ogre::Vector3( 1, 0, 0 );
        vy = Ogre::Vector3( 0, 1, 0 );
        vz = Ogre::Vector3( 0, 0, 1 );
    } else if (( i_direction & pm3::util::D_E ) != 0) {
        vx = Ogre::Vector3( 0, 0, -1 );
        vy = Ogre::Vector3( 0, 1, 0 );
        vz = Ogre::Vector3( 1, 0, 0 );
    } else if (( i_direction & pm3::util::D_S ) != 0) {
        vx = Ogre::Vector3( -1, 0, 0 );
        vy = Ogre::Vector3( 0, 1, 0 );
        vz = Ogre::Vector3( 0, 0, -1 );
    } else if (( i_direction & pm3::util::D_W ) != 0) {
        vx = Ogre::Vector3( 0, 0, 1 );
        vy = Ogre::Vector3( 0, 1, 0 );
        vz = Ogre::Vector3( -1, 0, 0 );
    } else if (( i_direction & pm3::util::D_U ) != 0) {
        vx = Ogre::Vector3( 1, 0, 0 );
        vy = Ogre::Vector3( 0, 0, -1 );
        vz = Ogre::Vector3( 0, 1, 0 );
    } else if (( i_direction & pm3::util::D_D ) != 0) {
        vx = Ogre::Vector3( 1, 0, 0 );
        vy = Ogre::Vector3( 0, 0, 1 );
        vz = Ogre::Vector3( 0, -1, 0 );
    }
    // Calculate the quaternion from the axes
    quat.FromAxes( vx, vy, vz );

    return quat;
}

bool pm3::util::has_tag( const std::string &r_string, const std::string &r_tag ) {
    std::vector<std::string> tokl = tokenize( r_string, tag_delimiters );

    for (size_t i=0; i<tokl.size(); i++)
        if (tokl [i] == r_tag)
            return true;

    return false;
}

std::string pm3::util::add_tag( const std::string &r_string, const std::string &r_tag ) {
    std::string str = r_string;
 
    if (!has_tag( r_string, r_tag )) {
        if (str.empty())
            str = r_tag;
        else
            str += tag_delimiter + r_tag;
    }

    return str;
}

std::string pm3::util::add_tags( const std::string &r_string, const std::string &r_tags ) {
    std::vector<std::string> tokl1 = tokenize( r_string, tag_delimiters );
    std::vector<std::string> tokl2 = tokenize( r_tags, tag_delimiters );
    std::string str = r_string;
    bool exists = false;

    for (size_t j=0; j<tokl2.size(); j++) {
        exists = false;

        for (size_t i=0; i<tokl1.size(); i++) {
            if (tokl1 [i] == tokl2 [j])
                exists = true;
        }

        if (!exists) {
            if (str.empty())
                str += tokl2 [j];
            else
                str += tag_delimiter + tokl2 [j];
        }
    }

    return str;
}

std::string pm3::util::remove_tag( const std::string &r_string, const std::string &r_tag ) {
    std::vector<std::string> tokl = tokenize( r_string, tag_delimiters );
    std::string str;
    size_t i = 0;

    // Search for the tag to be removed
    for (; i<tokl.size(); i++) {
        if (tokl [i] == r_tag)
            break;
    }

    // Was there such a tag? Erase it
    if (i != tokl.size())
        tokl.erase( tokl.begin() + i );

    // Recompile the taglist
    for (i=0; i<tokl.size(); i++) {
        str += tokl [i];

        if (i < tokl.size() - 1)
            str += tag_delimiter;
    }

    return str;
}

std::string pm3::util::remove_tags( const std::string &r_string, const std::string &r_tags ) {
    std::vector<std::string> tokl1 = tokenize( r_string, tag_delimiters );
    std::vector<std::string> tokl2 = tokenize( r_tags, tag_delimiters );
    std::string str;
    size_t i, j;

    for (j=0; j<tokl2.size(); j++) {
        // Search for any tokens that match
        for (i=0; i<tokl1.size(); i++) {
            if (tokl1 [i] == tokl2 [j])
                break;
        }
        // Was there such a token? Erase it then
        if (i != tokl1.size())
            tokl1.erase( tokl1.begin() + i );
    }

    // Recompile the taglist
    for (i=0; i<tokl1.size(); i++) {
        str += tokl1 [i];

        if (i < tokl1.size() - 1)
            str += tag_delimiter;
    }

    return str;
}

int pm3::util::str_to_int (const std::string &r_str) {
    int i = 0;

    std::istringstream iss (r_str);
    iss >> i;

    return i;
}

float pm3::util::str_to_float( const std::string &r_str ) {
    float f = 0.0;

    std::istringstream iss (r_str);
    iss >> f;

    return f;
}

std::string pm3::util::extract_dir( const std::string &r_path ) {
    // Find the position of the last slash
    size_t pos = r_path.find_last_of( "\\/" );
    // Cut the path and return the result
    return r_path.substr( 0, pos + 1 );
}

std::string pm3::util::strip_dir( const std::string &r_path, const std::string &r_dir ) {
    // Only strip the dir if it's exactly the same
    if ( r_path.compare( 0, r_dir.size(), r_dir ) == 0 )
        return r_path.substr( r_dir.size(), std::string::npos );

    return r_path;
}

bool pm3::util::has_dir( const std::string &r_path, const std::string &r_dir ) {
    return ( r_path.compare( 0, r_dir.size(), r_dir ) == 0 );
}

std::string pm3::util::get_str_type( const std::string &r_str ) {
    bool punct = false;
    bool numbers = false;
    bool exp = false;
    bool sign = false;
    bool unknown = false;
    std::string lower;

    for (size_t i=0; i<r_str.size(); i++) {
        if (isdigit( r_str [i] ))
            numbers = true;
        else if (r_str [i] == '.' || r_str [i] == ',')
            punct = true;
        else if (r_str [i] == '-' || r_str [i] == '+')
            sign = true;
        else if (tolower( r_str [i] ) == 'e')
            exp = true;
        else {
            lower += tolower( r_str [i] );
            unknown = true;
        }
    }

    if (unknown) {
        if (lower == "true" || lower == "false" || lower == "yes" || lower == "no")
            return STR_BOOLEAN;
        return STR_STRING;
    } else if (numbers && (punct || exp))
        return STR_FLOAT;
    return STR_INT;
}

float pm3::util::round( float f ) {
    return floor( f + 0.5f );
}

Ogre::Vector3 pm3::util::round( const Ogre::Vector3 &r_v ) {
    return Ogre::Vector3( round( r_v.x ), round( r_v.y ), round( r_v.z ) );
}

std::string pm3::util::pos_suffix( const Ogre::Vector3 &r_vec ) {
    std::string str = "_";
    // Round it, just in case
    Ogre::Vector3 vec = round( r_vec );
    // Build a string out of it
    str += to_str( vec.x ) + "_";
    str += to_str( vec.y ) + "_";
    str += to_str( vec.z );
    // And return it
    return str;
}

std::string pm3::util::trace_parents( Ogre::PCZSceneNode *p_node ) {
    std::string trace;

    while (p_node) {
        trace += p_node->getName();
        p_node = (Ogre::PCZSceneNode *) p_node->getParentSceneNode();

        if (p_node)
            trace += "<-";
    }

    return trace;
}

std::string pm3::util::trace_zones( Ogre::PCZSceneNode *p_node ) {
    std::string trace;
    Ogre::PCZone *zone = NULL;

    while (p_node) {
        zone = p_node->getHomeZone();

        if (zone)
            trace += zone->getName();
        else
            trace += "NULL";

        p_node = (Ogre::PCZSceneNode *) p_node->getParentSceneNode();

        if (p_node)
            trace += "<-";
    }

    return trace;
}

std::string pm3::util::indent_text( const std::string &r_indent, const std::string &r_text ) {
    std::string text = r_text;
    bool nline = false;

    for (size_t i=0; i<r_text.size(); i++) {
        if (r_text [i] == '\r' || r_text [i] == '\n') {
            nline = true;
        } else {
            if (nline)
                text = text.insert( i, r_indent );
        }
    }

    return text;
}

std::string pm3::util::color_to_hex( const Ogre::ColourValue &r_color ) {
    int r = (int) ( r_color.r * 255 );
    int g = (int) ( r_color.g * 255 );
    int b = (int) ( r_color.b * 255 );
    std::string str;
    char temp [10];

    sprintf( temp, "%02X", r ); temp [2] = 0;
    str += temp;
    sprintf( temp, "%02X", g ); temp [2] = 0;
    str += temp;
    sprintf( temp, "%02X", b ); temp [2] = 0;
    str += temp;

    return str;
}

